package io.noties.markwon.plugin.uml.model

import android.content.Context

class UmlProject( var name: String, context: Context
) {
    val umlClasses: ArrayList<UmlClass> = ArrayList()
    var umlClassCount: Int
    private var mUmlRelations: ArrayList<UmlRelation>
    private var mAppVersionCode = 0
    var zoom = 1f
    var xOffset = 0f
    var yOffset = 0f

    //    **********************************************************************************************
    //    Constructors
    //    **********************************************************************************************
    init {
        umlClassCount = 0
        mUmlRelations = ArrayList<UmlRelation>()
    }

    var umlRelations: ArrayList<UmlRelation>
        get() = mUmlRelations
        set(UmlRelations) {
            mUmlRelations = UmlRelations
        }

    fun getUmlClass(className: String): UmlClass? {
        for (c in umlClasses) if (c.name == className) return c
        return null
    }

    fun setAppVersionCode(appVersionCode: Int) {
        mAppVersionCode = appVersionCode
    }

    fun findClassByOrder(classOrder: Int): UmlClass? {
        for (c in umlClasses) if (c.classOrder == classOrder) return c
        return null
    }

    //    **********************************************************************************************
    //    Initialization
    //    **********************************************************************************************
    fun addUmlClass(umlClass: UmlClass) {
        umlClasses.add(umlClass)
        umlClassCount++
    }

    fun removeUmlClass(umlClass: UmlClass) {
        umlClasses.remove(umlClass)
        UmlType.removeUmlType(umlClass)
        removeRelationsInvolving(umlClass)
        removeAttributesOfType(umlClass as UmlType)
        removeMethodsOfType(umlClass as UmlType)
        removeParametersOfType(umlClass as UmlType)
    }

    fun addUmlRelation(umlRelation: UmlRelation) {
        mUmlRelations.add(umlRelation)
    }

    fun removeUmlRelation(umlRelation: UmlRelation) {
        mUmlRelations.remove(umlRelation)
    }

    fun removeRelationsInvolving(umlClass: UmlClass) {
        val umlRelations: ArrayList<UmlRelation> = ArrayList<UmlRelation>()
        for (r in mUmlRelations) {
            if (umlClass.isInvolvedInRelation(r)) umlRelations.add(r)
        }
        for (r in umlRelations) {
            removeUmlRelation(r)
        }
    }

    fun removeAttributesOfType(umlType: UmlType) {
        for (c in umlClasses) {
            for (a in c.attributes) {
                if (a.umlType == umlType) c.removeAttribute(a)
            }
        }
    }

    fun removeMethodsOfType(umlType: UmlType) {
        for (c in umlClasses) {
            for (m in c.methods) {
                if (m.umlType == umlType) c.removeMethod(m)
            }
        }
    }

    fun removeParametersOfType(umlType: UmlType) {
        for (c in umlClasses) {
            for (m in c.methods) {
                for (p in m.parameters) {
                    if (p.umlType == umlType) m.removeParameter(p)
                }
            }
        }
    }

    fun incrementClassCount() {
        umlClassCount++
    }

    //    **********************************************************************************************
    //    Test methods
    //    **********************************************************************************************
    fun relationAlreadyExistsBetween(firstClass: UmlClass, secondClass: UmlClass): Boolean {
        //check whether there already is a relation between two classes
        //this test is not oriented
        var test = false
        for (r in umlRelations)
            if (r.relationOriginClass == firstClass && r.relationEndClass == secondClass || (r.relationOriginClass == secondClass && r.relationEndClass == firstClass))
              test = true
        return test
    }

    fun hasConflictNameWith(umlClass: UmlClass): Boolean {
        var test = false
        for (c in umlClasses) if (c.name.compareTo(umlClass.name) == 0) test = true
        return test
    }

    fun containsClassNamed(className: String): Boolean {
        //check whether a class with className already exists in this project
        for (c in umlClasses) if (c.name == className) return true
        return false
    }

    fun mergeWith(project: UmlProject) {
        for (c in project.umlClasses) {
            while (UmlType.containsPrimitiveUmlTypeNamed(c.name)) c.name=(c.name + "(1)")
            while (UmlType.containsCustomUmlTypeNamed(c.name)) c.name=(c.name + "(1)")
            while (c.alreadyExists(this)) c.name=(c.name + "(1)")
            addUmlClass(c)
        }
        for (r in project.umlRelations) addUmlRelation(r)
    }

    companion object {
        const val JSON_PROJECT_NAME = "ProjectName"
        const val JSON_PROJECT_CLASS_COUNT = "ProjectClassCount"
        const val JSON_PROJECT_CLASSES = "ProjectClasses"
        const val JSON_PROJECT_RELATIONS = "ProjectRelations"
        const val JSON_PROJECT_PACKAGE_VERSION_CODE = "ProjectPackageVersionCode"
        const val JSON_PROJECT_ZOOM = "ProjectZoom"
        const val JSON_PROJECT_X_OFFSET = "ProjectXOffset"
        const val JSON_PROJECT_Y_OFFSET = "ProjectYOffset"
        const val PROJECT_DIRECTORY = "projects"
    }
}